﻿using System;
using System.Diagnostics;
using TMPro.Examples;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine.Rendering;



public class MinoMovementSystem : SystemBase {
    public EventHandler<float> OnFallDown;
    public Entity gameEntity;
    // public EventHandler<EventArgs> OnGameOver;

    private EndSimulationEntityCommandBufferSystem commandBufferSystem;

    private struct OnFallDownEvent {
        public float elapseTime;
    }

    protected override void OnCreate() {
        base.OnCreate();
        commandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
    protected override void OnUpdate() {
        var gridBuffer = EntityManager.GetBuffer<MinoGridState>(gameEntity);
        var istQueue = EntityManager.GetBuffer<MinoInstraction>(gameEntity);
        var mgEvent = GetComponent<MinoGridEvent>(gameEntity);
        var bornTime = GetComponent<MinoBornTime>(gameEntity);
        if (0 == istQueue.Length) {
            return;
        }
            // 根据 TheLastInstaction 和 Mino 当前的位置来判断是否达到边界
            bool collided = false;
            // float bornTime = -1f;
            bool needCallingFallDownEvent = false;
            bool isGameOver = false;
            int fallDistance = int.MaxValue;
            Entities.
                WithAll<DynamicMinoTag>().
                ForEach((ref Translation pos, ref LocalPosition localPosition, ref MinoRotationData shape) => {
                    int gridIdx = 0;
                    int minFallDist = 1;
                    switch (istQueue[0].Value) {
                        case InstractionType.Spawn: break;
                        // 检测在位置上是否与 StaticMino 有冲突，有就结束游戏；
                        case InstractionType.AutoMove:
                        case InstractionType.Down:
                            // 检测下沉一个单位时，是否会与 StaticMino 或者 Border 发生碰撞，有就把 Mino 设置为 Static；没有就下移一位
                            gridIdx = (int)((pos.Value.y - 1) * 12 + pos.Value.x);
                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
                                collided = true;
                                needCallingFallDownEvent = true;
                            }
                            if (gridIdx >= gridBuffer.Length) {
                                // 标记游戏结束
                                isGameOver = true;
                            }
                            pos.Value.y--;
                            break;
                        case InstractionType.Fall:
                            // 检测一下坠落点，并把 Mino 设置为 Static
                            do {
                                gridIdx = (int)((pos.Value.y - minFallDist) * 12 + pos.Value.x);
                                if (gridIdx > 0) {
                                    if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
                                        minFallDist--;
                                        break;
                                    }
                                    minFallDist++;
                                } else {
                                    minFallDist--;
                                    break;
                                }
                            } while (true);
                            fallDistance = math.min(minFallDist, fallDistance);
                            collided = true;
                            needCallingFallDownEvent = true;
                            break;
                        case InstractionType.Left:
                            // 检测左移一位是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就左移一位
                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x - 1);
                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
                                collided = true;
                            }
                            pos.Value.x--;
                            break;
                        case InstractionType.Right:
                            // 检测右移一位是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就右移一位
                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x + 1);
                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
                                collided = true;
                            }
                            pos.Value.x++;
                            break;
                        case InstractionType.Rotate:
                            // 检测旋转后是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就进行旋转
                            if (shape.typeValue == MinoRotateType.FourTimes) {
                                pos.Value -= localPosition.Value;
                                localPosition.Value = new int3(-localPosition.Value.y, localPosition.Value.x, localPosition.Value.z);
                                pos.Value += localPosition.Value;
                                shape.curRotateTimes = (shape.curRotateTimes + 1) % 4;
                            } else if (shape.typeValue == MinoRotateType.TwoTimes) {
                                int sign = shape.curRotateTimes == 0 ? 1 : -1;
                                shape.curRotateTimes = (shape.curRotateTimes + 1) % 2;

                                pos.Value -= localPosition.Value;
                                localPosition.Value = new int3(-localPosition.Value.y * sign, localPosition.Value.x * sign, localPosition.Value.z);
                                pos.Value += localPosition.Value;
                            }
                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
                                collided = true;
                            }
                            break;
                    }
                }).Run();

            if (true == collided) {
                /// 如果达到边界：
                /// 1. 修改 state 为 Static
                /// 2. 修改相应的 GridState
                /// 3. 触发形状生成事件
                if (needCallingFallDownEvent) {
                    float elapseTime = (float)Time.ElapsedTime;
                    OnFallDown?.Invoke(this, elapseTime - bornTime.Value);
                }
                // if (isGameOver) {
                //     OnGameOver?.Invoke(this, EventArgs.Empty);
                // }
                var cmdBuffer = commandBufferSystem.CreateCommandBuffer();
                var tmpGameEntity = gameEntity;
                Entities.
                    WithAll<DynamicMinoTag>().
                    ForEach((Entity minoEntity, ref Translation pos, ref LocalPosition localPosition, ref MinoRotationData shape) => {
                        int gridIdx = 0;
                        switch (istQueue[0].Value) {
                            case InstractionType.Spawn: break;
                            // 检测在位置上是否与 StaticMino 有冲突，有就结束游戏；
                            case InstractionType.AutoMove:
                            case InstractionType.Down:
                                // 撤销移动
                                pos.Value.y++;
                                // 修改 State 为 Static
                                cmdBuffer.RemoveComponent<DynamicMinoTag>(minoEntity);
                                cmdBuffer.AddComponent<StaticMinoTag>(minoEntity);

                                if (isGameOver) {
                                    cmdBuffer.SetComponent(tmpGameEntity, new MinoGridEvent { Value = MinoGridEventType.GameOver });
                                } else {
                                    // 修改相应的 GridState
                                    gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
                                    if (gridIdx < gridBuffer.Length) {
                                        var grid = gridBuffer[gridIdx];
                                        grid.Value = 1;
                                        gridBuffer[gridIdx] = grid;
                                        // 更新 GridEvent：生成新形状
                                        cmdBuffer.SetComponent(tmpGameEntity, new MinoGridEvent { Value = MinoGridEventType.FinishMoving });
                                    }
                                }
                                break;
                            case InstractionType.Fall:
                                // 移动（非撤销）
                                pos.Value.y -= fallDistance;
                                // 还是决定坠落后就立刻产生新物体，毕竟坠落后旋转并非常规操作
                                // // 正常坠落不修改为 Static，因为我希望 Fall 之后还可以旋转位移
                                // if (0 == fallDistance) {
                                // 修改 State 为 Static 
                                // state.Value = MinoStateType.Static;
                                cmdBuffer.RemoveComponent<DynamicMinoTag>(minoEntity);
                                cmdBuffer.AddComponent<StaticMinoTag>(minoEntity);

                                // 修改相应的 GridState
                                gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
                                if (!isGameOver && gridIdx < gridBuffer.Length) {
                                    var grid = gridBuffer[gridIdx];
                                    grid.Value = 1;
                                    gridBuffer[gridIdx] = grid;
                                    // 更新 GridEvent：生成新形状
                                    cmdBuffer.SetComponent(tmpGameEntity, new MinoGridEvent { Value = MinoGridEventType.FinishMoving });
                                } else {
                                    isGameOver = true;
                                    cmdBuffer.SetComponent(tmpGameEntity, new MinoGridEvent { Value = MinoGridEventType.GameOver });
                                }
                                // }
                                break;
                            // 检测一下坠落点，并把 Mino 设置为 Static
                            case InstractionType.Left:
                                // 撤销操作 
                                pos.Value.x++;
                                break;
                            case InstractionType.Right:
                                // 撤销操作 
                                pos.Value.x--;
                                break;
                            case InstractionType.Rotate:
                                // 因为碰到边界或其它 Mino 了，所以需要转回来
                                if (shape.typeValue == MinoRotateType.FourTimes) {
                                    shape.curRotateTimes = 0 == shape.curRotateTimes ? 3 : (shape.curRotateTimes - 1);

                                    pos.Value -= localPosition.Value;
                                    localPosition.Value = new int3(localPosition.Value.y, -localPosition.Value.x, localPosition.Value.z);
                                    pos.Value += localPosition.Value;
                                } else if (shape.typeValue == MinoRotateType.TwoTimes) {
                                    int sign = shape.curRotateTimes == 0 ? 1 : -1;
                                    shape.curRotateTimes = math.abs(shape.curRotateTimes--);

                                    pos.Value -= localPosition.Value;
                                    localPosition.Value = new int3(-localPosition.Value.y * sign, localPosition.Value.x * sign, localPosition.Value.z);
                                    pos.Value += localPosition.Value;
                                }
                                break;
                        }
                    }).Run();
                commandBufferSystem.AddJobHandleForProducer(Dependency);
            }
        // istQueue.Clear();
        if (istQueue.Length > 0) { 
            istQueue.RemoveAt(0);
        }

        //Entities
        //    .WithoutBurst()
        //    .ForEach((int entityInQueryIndex, Entity entity, ref DynamicBuffer<MinoGridState> buffer, in MinoGridEvent mgEvent) => {
        //        DynamicBuffer<MinoGridState> gridBuffer = buffer;
        //        // 根据 TheLastInstaction 和 Mino 当前的位置来判断是否达到边界
        //        bool collided = false;
        //        float bornTime = -1f;
        //        bool needCallingFallDownEvent = false;
        //        bool isGameOver = false;
        //        int fallDistance = int.MaxValue;
        //        Entities.
        //            ForEach((ref Translation pos, ref LocalPosition localPosition, ref MinoRotationData shape, in MinoState state, in TheLastInstraction instraction, in MinoBornTime minoBornTime) => {
        //                if (MinoStateType.Dynamic == state.Value) {
        //                    int gridIdx = 0;
        //                    int minFallDist = 1;
        //                    switch (instraction.Value) {
        //                        case Instraction.Spawn: break;
        //                        // 检测在位置上是否与 StaticMino 有冲突，有就结束游戏；
        //                        case Instraction.AutoMove:
        //                        case Instraction.Down:
        //                            // 检测下沉一个单位时，是否会与 StaticMino 或者 Border 发生碰撞，有就把 Mino 设置为 Static；没有就下移一位
        //                            gridIdx = (int)((pos.Value.y - 1) * 12 + pos.Value.x);
        //                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
        //                                collided = true;
        //                                bornTime = minoBornTime.Value;
        //                                needCallingFallDownEvent = true;
        //                            }
        //                            if (gridIdx >= gridBuffer.Length) {
        //                                // 标记游戏结束
        //                                isGameOver = true;
        //                            }
        //                            pos.Value.y--;
        //                            break;
        //                        case Instraction.Fall:
        //                            // 检测一下坠落点，并把 Mino 设置为 Static
        //                            do {
        //                                gridIdx = (int)((pos.Value.y - minFallDist) * 12 + pos.Value.x);
        //                                if (gridIdx > 0) {
        //                                    if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
        //                                        minFallDist--;
        //                                        break;
        //                                    } 
        //                                    minFallDist++;
        //                                } else {
        //                                    minFallDist--;
        //                                    break;
        //                                }
        //                            } while (true);
        //                            fallDistance = math.min(minFallDist, fallDistance);
        //                            collided = true;
        //                            bornTime = minoBornTime.Value;
        //                            needCallingFallDownEvent = true;
        //                            break;
        //                        case Instraction.Left:
        //                            // 检测左移一位是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就左移一位
        //                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x - 1);
        //                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
        //                                collided = true;
        //                            }
        //                            pos.Value.x--;
        //                            break;
        //                        case Instraction.Right:
        //                            // 检测右移一位是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就右移一位
        //                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x + 1);
        //                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
        //                                collided = true;
        //                            }
        //                            pos.Value.x++;
        //                            break;
        //                        case Instraction.Rotate:
        //                            // 检测旋转后是否会与 StaticMino 或者 Border 发生碰撞，有就不处理；没有就进行旋转
        //                            if (shape.typeValue == MinoRotateType.FourTimes) {
        //                                pos.Value -= localPosition.Value;
        //                                localPosition.Value = new int3(-localPosition.Value.y, localPosition.Value.x, localPosition.Value.z);
        //                                pos.Value += localPosition.Value;
        //                                shape.curRotateTimes = (shape.curRotateTimes + 1) % 4;
        //                            } else if (shape.typeValue == MinoRotateType.TwoTimes) {
        //                                int sign = shape.curRotateTimes == 0 ? 1 : -1;
        //                                shape.curRotateTimes = (shape.curRotateTimes + 1) % 2;

        //                                pos.Value -= localPosition.Value;
        //                                localPosition.Value = new int3(-localPosition.Value.y * sign, localPosition.Value.x * sign, localPosition.Value.z);
        //                                pos.Value += localPosition.Value;
        //                            }
        //                            gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
        //                            if (gridIdx < gridBuffer.Length && 1 == gridBuffer[gridIdx].Value) {
        //                                collided = true;
        //                            }
        //                            break;
        //                    }
        //                }
        //            }).Run();

        //        if (false == collided) {
        //            // 移动有效，则标记指令为 Idle
        //            Entities.
        //                ForEach((ref TheLastInstraction instraction, in MinoState state) => {
        //                    if (MinoStateType.Dynamic == state.Value) {
        //                        instraction.Value = Instraction.Idle;
        //                    }
        //                }).Schedule();
        //        } else {
        //            /// 如果达到边界：
        //            /// 1. 修改 state 为 Static
        //            /// 2. 修改相应的 GridState
        //            /// 3. 触发形状生成事件
        //            if (needCallingFallDownEvent) { 
        //                float elapseTime = (float)Time.ElapsedTime;
        //                OnFallDown?.Invoke(this, elapseTime - bornTime);
        //            }
        //            // if (isGameOver) {
        //            //     OnGameOver?.Invoke(this, EventArgs.Empty);
        //            // }
        //            var cmdBuffer = commandBufferSystem.CreateCommandBuffer();
        //            Entities.
        //                ForEach((ref MinoState state, ref Translation pos, ref TheLastInstraction instraction, ref LocalPosition localPosition, ref MinoRotationData shape) => {
        //                    if (MinoStateType.Dynamic == state.Value)
        //                    {
        //                        int gridIdx = 0;
        //                        switch (instraction.Value)
        //                        {
        //                            case Instraction.Spawn: break;
        //                            // 检测在位置上是否与 StaticMino 有冲突，有就结束游戏；
        //                            case Instraction.AutoMove:
        //                            case Instraction.Down:
        //                                // 撤销移动
        //                                pos.Value.y++;
        //                                // 修改 State 为 Static
        //                                state.Value = MinoStateType.Static;
        //                                if (isGameOver) {
        //                                    cmdBuffer.SetComponent(entity, new MinoGridEvent { Value = MinoGridEventType.GameOver });
        //                                } else {
        //                                    // 修改相应的 GridState
        //                                    gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
        //                                    if (gridIdx < gridBuffer.Length) {
        //                                        var grid = gridBuffer[gridIdx];
        //                                        grid.Value = 1;
        //                                        gridBuffer[gridIdx] = grid;
        //                                        // 更新 GridEvent：生成新形状
        //                                        cmdBuffer.SetComponent(entity, new MinoGridEvent { Value = MinoGridEventType.FinishMoving });
        //                                    } 
        //                                }
        //                                break;
        //                            case Instraction.Fall:
        //                                // 移动（非撤销）
        //                                pos.Value.y -= fallDistance;
        //                                // 还是决定坠落后就立刻产生新物体，毕竟坠落后旋转并非常规操作
        //                                // // 正常坠落不修改为 Static，因为我希望 Fall 之后还可以旋转位移
        //                                // if (0 == fallDistance) {
        //                                // 修改 State 为 Static 
        //                                state.Value = MinoStateType.Static;

        //                                // 修改相应的 GridState
        //                                gridIdx = (int)((pos.Value.y) * 12 + pos.Value.x);
        //                                if (!isGameOver && gridIdx < gridBuffer.Length) {
        //                                    var grid = gridBuffer[gridIdx];
        //                                    grid.Value = 1;
        //                                    gridBuffer[gridIdx] = grid;
        //                                    // 更新 GridEvent：生成新形状
        //                                    cmdBuffer.SetComponent(entity, new MinoGridEvent { Value = MinoGridEventType.FinishMoving });
        //                                } else {
        //                                    isGameOver = true;
        //                                    cmdBuffer.SetComponent(entity, new MinoGridEvent { Value = MinoGridEventType.GameOver });
        //                                }
        //                                // }
        //                                break;
        //                            // 检测一下坠落点，并把 Mino 设置为 Static
        //                            case Instraction.Left:
        //                                // 撤销操作 
        //                                pos.Value.x++;
        //                                break;
        //                            case Instraction.Right:
        //                                // 撤销操作 
        //                                pos.Value.x--;
        //                                break;
        //                            case Instraction.Rotate:
        //                                // 因为碰到边界或其它 Mino 了，所以需要转回来
        //                                if (shape.typeValue == MinoRotateType.FourTimes) {
        //                                    shape.curRotateTimes = 0 == shape.curRotateTimes? 3: (shape.curRotateTimes - 1);

        //                                    pos.Value -= localPosition.Value;
        //                                    localPosition.Value = new int3(localPosition.Value.y, -localPosition.Value.x, localPosition.Value.z);
        //                                    pos.Value += localPosition.Value;
        //                                } else if (shape.typeValue == MinoRotateType.TwoTimes) {
        //                                    int sign = shape.curRotateTimes == 0 ? 1 : -1;
        //                                    shape.curRotateTimes = math.abs(shape.curRotateTimes--);

        //                                    pos.Value -= localPosition.Value;
        //                                    localPosition.Value = new int3(-localPosition.Value.y * sign, localPosition.Value.x * sign, localPosition.Value.z);
        //                                    pos.Value += localPosition.Value;
        //                                }
        //                                break;
        //                        }
        //                        instraction.Value = Instraction.Idle;
        //                    }
        //                }).Run();
        //            commandBufferSystem.AddJobHandleForProducer(Dependency);
        //        }
        //}).Run();

    }

    // public static int3 RotateAroundPoint(int3 position, float3 pivot, float3 axis, float delta) => (int3)(math.mul(quaternion.AxisAngle(axis, delta), position - pivot) + pivot);
    // public static float3 RotateAroundPoint(float3 position, float3 pivot, float3 axis, float delta) => (int3)(math.mul(quaternion.AxisAngle(axis, delta), position - pivot) + pivot);
}